﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Data.Services.Client;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.Storage.RetryPolicies;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.Table.Protocol;


namespace cloudMPI
{
    public class TableData: TableEntity
    {
        public TableData() : base() { }

        public TableData(string partitionKey, string rowKey) : base(partitionKey, rowKey) { }

        public string Data { get; set; }
    }

    public class TableHelper
    {
        public CloudStorageAccount Account;
        public CloudTableClient TableClient;

        // Constructor - get settings from a hosted service configuration or .NET configuration file.

        public TableHelper(string connectionString)
        {
            Account = CloudStorageAccount.Parse(connectionString);
            TableClient = Account.CreateCloudTableClient();
            TableClient.RetryPolicy = new LinearRetry(TimeSpan.Zero, 4);
        }

  
        // List Tables.
        // Return true on success, false if not found, throw exception on error.

        // Create Table.
        // Return true on success, false if already exists, throw exception on error.

        public bool CreateTable(string tableName)
        {
            try
            {
                CloudTable table = TableClient.GetTableReference(tableName);
                table.CreateIfNotExists();
                return true;
            }
            catch (StorageException ex)
            {
                throw ex;
            }
        }

        // Delete Table.
        // Return true on success, false if not found, throw exception on error.

        public bool DeleteTable(string tableName)
        {
            try
            {
                CloudTable table = TableClient.GetTableReference(tableName);
                table.DeleteIfExists();
                return true;
            }
            catch (StorageException ex)
            {

                throw;
            }
        }

        // Insert entity.
        // Return true on success, false if not found, throw exception on error.

        public bool InsertEntity(string tableName, TableData obj)
        {
            try
            {
                CloudTable table = TableClient.GetTableReference(tableName);
                TableOperation insertOperation = TableOperation.Insert(obj);
                table.Execute(insertOperation);
                return true;
            }
            catch (StorageException ex)
            {
                throw;
            }
        }


        // Retrieve an entity.
        // Return true on success, false if not found, throw exception on error.

        public bool GetEntity<T>(string tableName, string partitionKey, string rowKey, out T entity) where T : TableEntity 
        {
            entity = null;

            try
            {
                var tableServiceContext = TableClient.GetTableServiceContext();
                IQueryable<T> entities = (from e in tableServiceContext.CreateQuery<T>(tableName) 
                                                        where (e.PartitionKey == partitionKey && e.RowKey == rowKey) 
                                                        select e);

                entity = entities.FirstOrDefault();

                return true;
            }
            catch (DataServiceRequestException)
            {
                return false;
            }
            catch (StorageException ex)
            {
                throw;
            }
        }

        
        // Query entities. Use LINQ clauses to filter data.
        // Return true on success, false if not found, throw exception on error.

        public DataServiceQuery<T> QueryEntities<T>(string tableName) where T : TableEntity 
        {
            var tableServiceContext = TableClient.GetTableServiceContext();
            return tableServiceContext.CreateQuery<T>(tableName);
        }
            

        // Replace Update entity. Completely replace previous entity with new entity.
        // Return true on success, false if not found, throw exception on error.

        //public bool ReplaceUpdateEntity<T>(string tableName, string partitionKey, string rowKey, T obj) where T : TableEntity
        //{
        //    try
        //    {
        //        var tableServiceContext = TableClient.GetTableServiceContext();
        //        IQueryable<T> entities = (from e in tableServiceContext.CreateQuery<T>(tableName)
        //                                  where e.PartitionKey == partitionKey && e.RowKey == rowKey
        //                                  select e);

        //        T entity = entities.FirstOrDefault();

        //        Type t = obj.GetType();
        //        PropertyInfo[] pi = t.GetProperties();

        //        foreach (PropertyInfo p in pi)
        //        {
        //            p.SetValue(entity, p.GetValue(obj, null), null);
        //        }

        //        tableServiceContext.UpdateObject(entity);
        //        tableServiceContext.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);

        //        return true;
        //    }
        //    catch (DataServiceRequestException)
        //    {
        //        return false;
        //    }
        //    catch (StorageException ex)
        //    {
                

        //        throw;
        //    }
        //}

        //Unconditional update
        public bool UnconditionalUpdate<T>(string tableName, string partitionKey, string rowKey, string newData) where T : TableEntity
        {
            try
            {
                TableData entity = new TableData();
                GetEntity<TableData>(tableName, partitionKey, rowKey, out entity);
                var tableServiceContext = TableClient.GetTableServiceContext();
                entity.Data = newData;
                tableServiceContext.AttachTo(tableName, entity, "*");
                tableServiceContext.UpdateObject(entity);
                tableServiceContext.SaveChangesWithRetries();
                return true;
            }
            catch
            {
                return false;
            }

        }

        // Merge update an entity (preserve previous properties not overwritten).
        // Return true on success, false if not found, throw exception on error.

        //public bool MergeUpdateEntity<T>(string tableName, string partitionKey, string rowKey, T obj) where T : TableEntity
        //{
        //    try
        //    {
        //        var tableServiceContext = TableClient.GetTableServiceContext();
        //        IQueryable<T> entities = (from e in tableServiceContext.CreateQuery<T>(tableName)
        //                                  where e.PartitionKey == partitionKey && e.RowKey == rowKey
        //                                  select e);

        //        T entity = entities.FirstOrDefault();

        //        Type t = obj.GetType();
        //        PropertyInfo[] pi = t.GetProperties();

        //        foreach (PropertyInfo p in pi)
        //        {
        //            p.SetValue(entity, p.GetValue(obj, null), null);
        //        }

        //        tableServiceContext.UpdateObject(entity);
        //        tableServiceContext.SaveChanges();

        //        return true;
        //    }
        //    catch (DataServiceRequestException)
        //    {
        //        return false;
        //    }
        //    catch (StorageClientException ex)
        //    {
        //        if ((int)ex.StatusCode == 404)
        //        {
        //            return false;
        //        }

        //        throw;
        //    }
        //}


        // Delete entity.
        // Return true on success, false if not found, throw exception on error.

        public bool DeleteEntity<T>(string tableName, string partitionKey, string rowKey) where T : TableEntity
        {
            var context = TableClient.GetTableServiceContext();
            try
            {
                CloudTable table = TableClient.GetTableReference(tableName);
                IQueryable<T> entities = (from e in context.CreateQuery<T>(tableName)
                                          where (e.PartitionKey == partitionKey && e.RowKey == rowKey)
                                          select e);

                T entity = entities.FirstOrDefault();
                if (entities != null)
                {
                    TableOperation deleteOperation = TableOperation.Delete(entity);
                    table.Execute(deleteOperation);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (DataServiceRequestException)
            {
                return false;
            }
            catch (StorageException ex)
            {
                throw;
            }
        }

    }
}


